package luke.ui.components;

import luke.audio.metadata.AudioMetadataReader;

import java.io.File;
import java.util.*;

/**
 * 歌曲水桶类，用于管理歌曲选择状态
 * 实现统一的水桶算法，不断接收歌曲列表，直到达到目标时长
 */
public class SongBucket {
    // 源集合，存储选中的歌曲，使用Set确保唯一性
    private Set<File> sourceSet;
    // 目标时长（毫秒）
    private long targetDurationMs;
    // 元数据缓存，避免重复读取
    private Map<File, AudioMetadataReader.AudioMetadata> metadataCache;
    
    /**
     * 构造函数
     * 
     * @param targetDurationMinutes 目标时长（分钟）
     */
    public SongBucket(double targetDurationMinutes) {
        this.sourceSet = new LinkedHashSet<>(); // 使用LinkedHashSet保持插入顺序
        // 目标时长乘以2，因为音频变速最高不会超过2倍
        this.targetDurationMs = (long) (targetDurationMinutes * 60 * 1000 * 2);
        // 初始化元数据缓存
        this.metadataCache = new HashMap<>();
    }
    
    /**
     * 向桶中添加歌曲列表
     * 
     * @param songs 要处理的歌曲列表
     * @return 如果桶已满，返回true；如果未满，返回false
     */
    public boolean processSongs(List<File> songs) {
        // 遍历传入的歌曲列表
        for (File song : songs) {
            // 检查歌曲是否已经存在于桶中，避免重复添加
            if (sourceSet.contains(song)) {
                continue; // 如果已存在，跳过这首歌
            }
            
            // 粗略计算歌曲时长（先尝试读取元数据，再根据文件大小估算）
            int songDuration = estimateDuration(song);
            
            // 计算添加这首歌后的总时长
            long currentDuration = getCurrentDuration();
            long newDuration = currentDuration + songDuration;
            
            // 检查是否达到目标时长
            if (newDuration >= targetDurationMs) {
                // 如果添加这首歌会超过目标时长，则不添加这首歌
                // 达到目标时长，返回true表示桶已满
                return true;
            }
            
            // 添加到源集合
            sourceSet.add(song);
        }
        
        // 未达到目标时长，返回false表示桶未满
        return false;
    }
    
    /**
     * 粗略计算歌曲时长（先尝试读取元数据，再根据文件大小估算）
     * 
     * @param audioFile 音频文件
     * @return 估算的时长（毫秒）
     */
    private int estimateDuration(File audioFile) {
        try {
            // 先从缓存中查找元数据
            AudioMetadataReader.AudioMetadata metadata = metadataCache.get(audioFile);
            
            // 如果缓存中没有，则读取元数据并存入缓存
            if (metadata == null) {
                metadata = AudioMetadataReader.readMetadata(audioFile);
                metadataCache.put(audioFile, metadata);
            }
            
            if (metadata != null && metadata.getDuration() != null && !metadata.getDuration().isEmpty()) {
                // 解析元数据中的时长（格式为 mm:ss）
                String durationStr = metadata.getDuration();
                String[] parts = durationStr.split(":");
                if (parts.length == 2) {
                    int minutes = Integer.parseInt(parts[0]);
                    int seconds = Integer.parseInt(parts[1]);
                    return (minutes * 60 + seconds) * 1000; // 转换为毫秒
                }
            }
        } catch (Exception e) {
            // 如果读取元数据失败，继续尝试根据文件大小估算
            System.err.println("读取元数据失败: " + e.getMessage());
        }
        
        try {
            // 根据文件大小估算时长
            // 获取文件大小（字节）
            long fileSize = audioFile.length();
            
            // 根据文件大小估算时长
            // 假设平均比特率为128 kbps（16 KB/s）
            // 时长（秒）= 文件大小（KB）/ 16
            // 时长（毫秒）= 文件大小（字节）/ 16 * 1000 / 1024
            long estimatedDurationMs = (fileSize * 1000) / (16 * 1024);
            
            // 确保估算的时长在合理范围内（1秒到1小时）
            if (estimatedDurationMs < 1000) {
                estimatedDurationMs = 1000; // 最少1秒
            } else if (estimatedDurationMs > 3600000) {
                estimatedDurationMs = 3600000; // 最多1小时
            }
            
            return (int) estimatedDurationMs;
        } catch (Exception e) {
            // 如果无法获取文件大小，返回默认时长（3分钟）
            return 180000; // 3分钟 = 180000毫秒
        }
    }
    
    /**
     * 获取源列表（选中的歌曲）
     * 
     * @return 源列表
     */
    public List<File> getSourceList() {
        // 将Set转换为List返回，保持插入顺序
        return new ArrayList<>(sourceSet);
    }
    
    /**
     * 清空源集合
     */
    public void clearSourceList() {
        sourceSet.clear();
    }
    
    /**
     * 清空元数据缓存
     */
    public void clearMetadataCache() {
        metadataCache.clear();
    }
    
    /**
     * 计算当前桶中的总时长
     * 
     * @return 当前桶中的总时长（毫秒）
     */
    private long getCurrentDuration() {
        long totalDuration = 0;
        for (File file : sourceSet) {
            totalDuration += estimateDuration(file);
        }
        return totalDuration;
    }
}